package ni;

import chatsystemTDa2.*;
import controller.Msg;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * This class handles sending messages via UDP
 */
public class UDPSender extends Thread{

	/**
	 * socket port number
	 * link to the ChatNI
	 * link to the model: the queue of messages to send
	 * a boolean to close the thread
	 */
	private final int port = 16050;
	private DatagramSocket sock;
	private ChatNI chatNI;
	private LinkedBlockingQueue <Msg> listMsg;
	private volatile boolean active;
	
	/**
	 * @param chatNI is a link to the chatNI
	 * @param listMsg is a link to the queue of messages to send
	 */
	public UDPSender(ChatNI chatNI, LinkedBlockingQueue <Msg> listMsg) {
		this.sock = null;
		this.chatNI = chatNI;
		this.listMsg = listMsg;
		this.active = true;
	}
	
	/**
	 * sends a message via UDP
	 * We open and close the socket at every sent message
	 * We send the message on a predefined port number
	 * Messages are serialized
	 * @param message is the message to send
	 * @param ip is the ip address to send the message to
	 */
	public void send(Message message, InetAddress ip) {
			try {
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				BufferedOutputStream bos = new BufferedOutputStream(baos);
				ObjectOutputStream oos = new ObjectOutputStream(bos);
				oos.flush();
				oos.writeObject(message);
				oos.flush();
				byte[] obj = baos.toByteArray();
				
				oos.close();
				bos.close();
				baos.close();
				DatagramPacket dp = new DatagramPacket(obj, obj.length, ip, port);
				
				sock = new DatagramSocket();
				sock.send(dp);
				sock.close();
				System.out.println("sent : "+message);
			} catch (IOException e) {e.printStackTrace();
			} 
	}
	
	/**
	 * closing the thread
	 * this.interrupt() allows to wake the thread up from the blocking call listMsg.take()
	 * listMsg is the queue of messages to send from the model.
	 */
	public void close(){
		this.active = false;
		this.interrupt();
		if (this.sock != null){
			this.sock.close();
		}
	}
	
	/**
	 * We pop the message to send from the queue listMsg with take()
	 * take() is a blocking call, if the queue is empty, it waits for an element to be added
	 */
	public void run(){
		while(this.active){
			try {
				Msg m = listMsg.take();
				send(m.getMsg(), m.getIp());
			} catch (InterruptedException e) {System.out.println("Fermeture Thread Send");}
		}
	}
}
